﻿#region File Description
///////////////////////////////////////////////////////////////////////////////
/// \file GOLEcosystem.cs
/// \brief This file stores the GOLEcosystem class
/// \author Bradley Mclain, Steven Duda
/// \date 16/5/2010
/// \remarks Copyright(c) 2010 LM01 Project Group (Animus Games - ViralFlux)
/// \remarks Some Rights Reserved.
/// Licenced under: Microsoft Reciprocal License (Ms-RL)
/// For More Information see: www.microsoft.com/opensource/licenses.mspx
///////////////////////////////////////////////////////////////////////////////
#endregion

#region Using Statements
    using System.Collections.Generic;
    using PropagationEngine;
#endregion

namespace GameOfLife
{
    /// <summary>
    /// \class GOLEcosystem
    /// \brief This class provides the Ecosystem for Game Of Life
    /// \note This class allows Ecosystem to be saved and loaded from various formats
    /// \author Bradley Mclain, Steven Duda
    /// \date 19/5/2010
    /// </summary>
	internal sealed class GOLEcosystem : Ecosystem
    {
        private static GOLEcosystem instance = new GOLEcosystem();

        #region Fields

        private static uint NumberOfCells;
        private static uint GridWidth;
        private static uint GridHeight;

        #endregion

        #region Methods

        /// \brief Loads a GameofLife Ecosystem based on Dimensions & Defaults
        /// \param WidthDimension The GridWidth of the Cell Grid
        /// \param HeightDimension The GridHeight of the Cell Grid
        /// \note This can be used to Load an Ecosystem without XML
        /// \remarks LoadFromDimensions() calls Reset()
        /// \author Steven Duda
        /// \date 19/5/2010
        internal static void LoadFromDimensions(uint WidthDimension, uint HeightDimension)
        {
            Reset();
            GridWidth = WidthDimension;
            GridHeight = HeightDimension;
            NumberOfCells = (GridWidth * GridHeight);

            //Create default Game of Life entity and insert
            Entity entity = new Entity(1,0,0);
            GOLEcosystem.entities.Add(entity.ID, entity);
 
            //For each Interface cell, Create a new Node
            // \note START FROM 1 (There is NO CELL(node) 0(zero))
            for (uint i = 1; i <= NumberOfCells; ++i)
            {
                //Create the node, add GOLProperties and insert it
                Node node = new Node(i);
                GOLNodeProperties properties = new GOLNodeProperties();
                properties.State = GOLNodeState.Dead;
                node.Properties = properties;
                node.Transmit(new Transmission(entity,0));
                GOLEcosystem.nodes.Add(node.ID, node);
            }

            //Generate each node's edges (using standard Grid Pattern)
            for (uint i = 1; i <= NumberOfCells; ++i)
            {
                CreateCellEdges(i);
            }
            
            //For each node setup its incoming and outgoing edges
            foreach (KeyValuePair<uint, Node> kvp in GOLEcosystem.nodes)
            {
                Node node = kvp.Value;
                node.IncomingEdges = GOLEcosystem.GetIncomingEdgesForNode(node);
                node.OutgoingEdges = GOLEcosystem.GetOutgoingEdgesForNode(node);
            }
        }

        internal static void ToggleGOLCellExistance(uint NodeID)
        {
            if (nodes.ContainsKey(NodeID))
            {
                //Remove Edges 
                DeleteCellEdges(NodeID);

                //Remove Node
                nodes.Remove(NodeID);
                NumberOfCells--;
            }
            else
            {
                //Re-Create the Node
                Node node = new Node(NodeID);
                GOLNodeProperties properties = new GOLNodeProperties();
                Entity entity = GetEntityByID(1);   // Entity ID:1 is the Default GOL Entity
                properties.State = GOLNodeState.Dead;
                node.Properties = properties;
                node.Transmit(new Transmission(entity, 0));
                GOLEcosystem.nodes.Add(node.ID, node);
                NumberOfCells++;

                //Re-Create Edges
                CreateCellEdges(NodeID);

            }
        }

        private static void CreateCellEdges(uint NodeID)
        {
            Edge edge;
            if (NodeID % GridWidth != 0)
            {
                //Join Horizontal (Left-to-right)
                edge = new Edge(0, GetNodeByID(NodeID), GetNodeByID(NodeID + 1));
                GOLEcosystem.edges.Add(edge);

                //Join Horizontal (Right-to-Left)
                edge = new Edge(0, GetNodeByID(NodeID + 1), GetNodeByID(NodeID));
                GOLEcosystem.edges.Add(edge);
            }
            if (NodeID <= (NumberOfCells - GridWidth))
            {
                //Join Vertical (Down)
                edge = new Edge(0, GetNodeByID(NodeID), GetNodeByID(NodeID + GridWidth));
                GOLEcosystem.edges.Add(edge);

                //Join Vertical (Up)
                edge = new Edge(0, GetNodeByID(NodeID + GridWidth), GetNodeByID(NodeID));
                GOLEcosystem.edges.Add(edge);
            }
            // Join Down Right (and Up Left) Diagonal
            if ((NodeID % GridWidth) != 0 && (NodeID < (NumberOfCells - GridWidth)))
            {
                edge = new Edge(0, GetNodeByID(NodeID), GetNodeByID(NodeID + GridWidth + 1));
                GOLEcosystem.edges.Add(edge);
                edge = new Edge(0, GetNodeByID(NodeID + GridWidth + 1), GetNodeByID(NodeID));
                GOLEcosystem.edges.Add(edge);
            }
            // Join Down Left (and Up Right) Diagonal
            if ((NodeID % GridWidth) != 1 && (NodeID <= (NumberOfCells - GridWidth)))
            {
                edge = new Edge(0, GetNodeByID(NodeID), GetNodeByID(NodeID + GridWidth - 1));
                GOLEcosystem.edges.Add(edge);
                edge = new Edge(0, GetNodeByID(NodeID + GridWidth - 1), GetNodeByID(NodeID));
                GOLEcosystem.edges.Add(edge);
            }
        }

        //Remove Edges of a specified Node
        private static void DeleteCellEdges(uint NodeID)
        {
            Edge edge;

            if (NodeID % GridWidth != 0)
            {
                //Delete Horizontal (Left-to-right)
                edge = new Edge(0, GetNodeByID(NodeID), GetNodeByID(NodeID + 1));
                GOLEcosystem.edges.Remove(edge);

                //Delete Horizontal (Right-to-Left)
                edge = new Edge(0, GetNodeByID(NodeID + 1), GetNodeByID(NodeID));
                GOLEcosystem.edges.Remove(edge);
            }
            if (NodeID <= (NumberOfCells - GridWidth))
            {
                //Delete Vertical (Down)
                edge = new Edge(0, GetNodeByID(NodeID), GetNodeByID(NodeID + GridWidth));
                GOLEcosystem.edges.Remove(edge);

                //Delete Vertical (Up)
                edge = new Edge(0, GetNodeByID(NodeID + GridWidth), GetNodeByID(NodeID));
                GOLEcosystem.edges.Remove(edge);
            }
            // Delete Down Right (and Up Left) Diagonal
            if ((NodeID % GridWidth) != 0 && (NodeID < (NumberOfCells - GridWidth)))
            {
                edge = new Edge(0, GetNodeByID(NodeID), GetNodeByID(NodeID + GridWidth + 1));
                GOLEcosystem.edges.Remove(edge);
                edge = new Edge(0, GetNodeByID(NodeID + GridWidth + 1), GetNodeByID(NodeID));
                GOLEcosystem.edges.Remove(edge);
            }
            // Delete Down Left (and Up Right) Diagonal
            if ((NodeID % GridWidth) != 1 && (NodeID <= (NumberOfCells - GridWidth)))
            {
                edge = new Edge(0, GetNodeByID(NodeID), GetNodeByID(NodeID + GridWidth - 1));
                GOLEcosystem.edges.Remove(edge);
                edge = new Edge(0, GetNodeByID(NodeID + GridWidth - 1), GetNodeByID(NodeID));
                GOLEcosystem.edges.Remove(edge);
            }
        }

        /// \brief Sets the state of an individual Node (with GOLNodeProperties)
        /// \param NodeID The identifier(ID) of the node to set state
        /// \param state The GOLNodeState to apply to the target node
        /// \note The IF-Statement to check if the Node exists is simply optional safety
        /// \author Steven Duda
        /// \date 19/5/2010
        internal static void SetNodeState(uint NodeID, GOLNodeState state)
        {
            if (nodes.ContainsKey(NodeID))
                ((GOLNodeProperties)nodes[NodeID].Properties).State = state;
        }

        /// \brief Gets the state of an individual Node (with GOLNodeProperties)
        /// \param NodeID The identifier(ID) of the node to get state
        /// \return GOLNodeState The current state of the target node
        /// \note Warning: Does not Check if the Node Exists!
        /// \author Steven Duda
        /// \date 19/5/2010
        internal static GOLNodeState GetNodeState(uint NodeID)
        {
            return ((GOLNodeProperties)nodes[NodeID].Properties).State;
        }

        /// \brief Gets the state of an individual Node (with GOLNodeProperties) Safely
        /// \param NodeID The identifier(ID) of the node to get state
        /// \return GOLNodeState The current state of the target node
        /// \author Steven Duda
        /// \date 24/9/2010
        internal static GOLNodeState SafeGetNodeState(uint NodeID)
        {
            if (nodes.ContainsKey(NodeID))
                return ((GOLNodeProperties)nodes[NodeID].Properties).State;
            else
                return (GOLNodeState.DoesNotExist);
        }

        public override void SaveToBinary(string filename)
        {
        }
		public override bool LoadFromBinary(string filename)
        {
            return false;
        }
        public override bool LoadFromXML(string filename)
        {
            return false;
        }
		public override void SaveToXML(string filename)
        {
        }

        internal static void AddRuleset()
        {
            ruleset = new GOLRuleset();
        }

        // Adds a Generic Life Cellular Automation Ruleset
        // See: http://en.wikipedia.org/wiki/Life-like_cellular_automaton
        internal static void AddGenericRuleset(List<int> births, List<int> survivals)
        {
            ruleset = new GOLRulesetGeneric();
            ((GOLRulesetGeneric)ruleset).Birth = births;
            ((GOLRulesetGeneric)ruleset).Survival = survivals;
        }


        #endregion

    }
}
